/*
 *Copyright (c) 2024 Black Sesame Technologies
 *
 *Licensed under the Apache License, Version 2.0 (the "License");
 *you may not use this file except in compliance with the License.
 *You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 *Unless required by applicable law or agreed to in writing, software
 *distributed under the License is distributed on an "AS IS" BASIS,
 *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *See the License for the specific language governing permissions and
 *limitations under the License.
*/

#include "eth2can.h"
#include "switch0_canoe_ipc_client.h"
#include "can_output.h"
#include "VCan_Command.h"
#include "hscg_firmware_statistic.h"

extern switch0_canoe_ipc_client_t *can_client;
lwrb_t eth_2_can_q;
lwrb_t can_2_eth_q, can_2_eth_data;
volatile uint32* eth_2_can_q_read_ptr = (uint32*)ETH_2_CAN_QUEUE_READ_PTR;
volatile uint32* eth_2_can_q_write_ptr = (uint32*)ETH_2_CAN_QUEUE_WRITE_PTR;

void check_eth2can_queue() {
    uint16 ele_count;
    ETH_2_CAN_QE *target_e = (ETH_2_CAN_QE*)(*eth_2_can_q_write_ptr);
    ETH_2_CAN_QE *now_e = (ETH_2_CAN_QE*)lwrb_get_linear_block_write_address(&eth_2_can_q);
    ele_count = util_compute_len_between_two_ring_ptr((uint8*)now_e, (uint8*)target_e, 
                                                    sizeof(ETH_2_CAN_QE), (uint8*)ETH_2_CAN_QUEUE_SRAM_ADDR,
                                                    (uint8*)ETH_2_CAN_QUEUE_SRAM_ADDR_END); 
    if(ele_count > 0) {
        hprintf(DEBUG, "eth2can_enqueue data:0x%x  now_e:0x%x sizeof(ETH_2_CAN_QE):%d advance:%d\n", *eth_2_can_q_write_ptr, now_e, sizeof(ETH_2_CAN_QE), ele_count);
        lwrb_advance(&eth_2_can_q, ele_count*sizeof(ETH_2_CAN_QE));
    }
}

// 主处理循环，处理队列中的消息
int eth2can_enqueue_main_process() {
    Flexcan_Ip_MsgBuffType *msg;
    ETH_2_CAN_QE e = {0};
    int ret, br;
    uint32 cs;
    uint8 dlc;

    //检查eth核有没有写数据
    check_eth2can_queue();

    while (1) {
        ret = lwrb_read_ex(&eth_2_can_q, &e, sizeof(ETH_2_CAN_QE), &br, LWRB_FLAG_READ_ALL);
        if(ret == 0) {
            break;
        }
        if (br != sizeof(ETH_2_CAN_QE)) {
            hprintf(DEBUG, "warning: wrong length to read: %d, should be: %d\n", br, sizeof(ETH_2_CAN_QE));
            continue;
        }

        STATISTICS_UPDATE_PACKET_ETH2CAN_LOOP();
        STATISTICS_UPDATE_PACKET_ETH2CAN_TIMESTAMP(TIMESTAMP_PACKET_ETH2CAN_RECV_SQBUFFER);
        STATISTICS_UPDATE_PACKET_ETH2CAN_DATA(e.can_identify, (uint8 *) e.data_ptr, e.datalen);

        STATS_INC_ETH_RX();

        hprintf(DEBUG, "CAN recv SQE dataptr: 0x%x, len: %d, SAMC: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x, rtr: %d, eff: %d, brs: %d, fdf: %d, esi: %d, bus: %d\n",
                e.data_ptr, e.datalen,
                *((uint8*)e.data_ptr + 0),
                *((uint8*)e.data_ptr + 1),
                *((uint8*)e.data_ptr + 2),
                *((uint8*)e.data_ptr + 3),
                *((uint8*)e.data_ptr + 4),
                *((uint8*)e.data_ptr + 5),
                e.rtr, e.eff, e.brs, e.fdf, e.esi, e.can_bus_id);

        dlc = (e.fdf == 1) ? utils_canfd_datalen_to_dlc(e.datalen) : e.datalen;
        cs = CAN_BUILD_CS_ATTR(dlc, e.rtr, e.eff, 0, e.esi, e.brs, e.fdf);
        ret = can_output_queue_send(e.can_bus_id, e.can_identify, cs, (uint8 *)e.data_ptr, e.datalen);
        if (ret) {
            STATS_INC_ETH_RX_DROP();
        }

        uint8* temp_ptr = lwrb_get_linear_block_read_address(&eth_2_can_q);
        *eth_2_can_q_read_ptr = (uint32)temp_ptr;
    }

    return 0;
}

// 运行eth2can的主函数
void eth2can_run(void) 
{
    eth2can_enqueue_main_process();
}

// IPC回调函数：消息入队
void eth2can_enqueue_cb(const uint32_t element_ptr, const uint16_t element_count, char **message,
                        void *err, const ext_info_t *info) {
    hprintf(DEBUG, "error: not support eth2can_enqueue\n");
}

// IPC回调函数：消息出队
void eth2can_dequeue_cb(const uint32_t element_ptr, const uint16_t element_count, char **message,
                        void *err, const ext_info_t *info) {
    hprintf(DEBUG, "error: not support eth2can_dequeue\n");
}

// 初始化eth2can模块
void eth2can_init(void) 
{
    lwrb_init(&eth_2_can_q, (void*)ETH_2_CAN_QUEUE_SRAM_ADDR, MAX_ETH_2_CAN_DATA_LEN * sizeof(ETH_2_CAN_QE));
    *(eth_2_can_q_read_ptr) = (uint32_t)lwrb_get_linear_block_read_address(&eth_2_can_q);
    *(eth_2_can_q_write_ptr) = (uint32_t)lwrb_get_linear_block_write_address(&eth_2_can_q);
    lwrb_init(&can_2_eth_q, (void*)CAN_2_ETH_QUEUE_SRAM_ADDR, MAX_CAN_2_ETH_QUEUE_LEN * sizeof(CAN_2_ETH_QE));
    lwrb_init(&can_2_eth_data, (void*)CAN_2_ETH_DATA_SRAM_ADDR, MAX_CAN_DATA * MAX_CAN_2_ETH_DATA_LEN);

    hprintf(DEBUG, "init eth_2_can_q at 0x%x success, size: 0x%x\n", ETH_2_CAN_QUEUE_SRAM_ADDR, 512 * sizeof(ETH_2_CAN_QE));
    hprintf(DEBUG, "init can_2_eth_q at 0x%x success, size: 0x%x\n", CAN_2_ETH_QUEUE_SRAM_ADDR, MAX_CAN_2_ETH_QUEUE_LEN * sizeof(CAN_2_ETH_QE));
}
